/**
 * 
 */
package com.study.nio.manyPeopleChat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;

/**
 * @ClassName: ChatServer
 * @Description: nio多人聊天服务端
 * @author zhuqiang
 * @date 2018年12月26日 下午4:12:40
 */
public class ChatServer {
	private Selector selector;
	private ServerSocketChannel listenerChannel;
	private static final int PORT = 9999;

	public ChatServer() {
		try {
			selector = Selector.open();
			listenerChannel = ServerSocketChannel.open();
			listenerChannel.bind(new InetSocketAddress(PORT));
			listenerChannel.configureBlocking(false);
			listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
			printInfo("Chat Server is ready...");

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void start() {
		try {
			while (true) {
				int count = selector.select();
				if (count > 0) {
					Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
					while (iterator.hasNext()) {
						SelectionKey key = iterator.next();
						if (key.isAcceptable()) {
							SocketChannel sc = listenerChannel.accept();
							// 非阻塞模式
							sc.configureBlocking(false);
							// 注册到选择器上并监听read
							sc.register(selector, SelectionKey.OP_READ);
							System.out.println(sc.getRemoteAddress().toString().substring(1) + "上线了...");
							// 将此对应的channel 设置为accept,接着准备接受其他客户端请求
							key.interestOps(SelectionKey.OP_ACCEPT);
						}
						// 监听到read
						if (key.isReadable()) {
							readMsg(key); // 读取客户端发来的数据
						}
						// 一定要把当前key 删掉，防止重复处理
						iterator.remove();
					}

				} else {
					System.out.println("独自在寒风中等候...");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param key
	 */
	private void readMsg(SelectionKey key) {
		SocketChannel channel = null;
		try {
			// 得到关联的通道
			channel = (SocketChannel) key.channel();
			// 设置buffer 缓冲区
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			// 从通道中读取数据并存储到缓冲区中
			int count = channel.read(buffer);
			// 如果读取到了数据
			if (count > 0) {
				// 把缓冲区数据转换为字符串
				String msg = new String(buffer.array());
				printInfo(msg);
				// 将关联的channel 设置为read，继续准备接受数据
				key.interestOps(SelectionKey.OP_READ);
				broadCast(channel, msg); // 向所有客户端广播数据
			}
			buffer.clear();
		} catch (Exception e) {
			try {
				// 当客户端关闭channel 时，进行异常如理
				printInfo(channel.getRemoteAddress().toString().substring(1) + "下线了...");
				key.cancel(); // 取消注册
				channel.close(); // 关闭通道
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}

	/**
	 * @param channel
	 * @param msg
	 * @throws IOException 
	 */
	private void broadCast(SocketChannel except, String msg) throws IOException {
		System.out.println("发送广播...");
		// 广播数据到所有的SocketChannel 中
		for (SelectionKey key : selector.keys()) {
			Channel targetchannel = key.channel();
			// 排除自身
			if (targetchannel instanceof SocketChannel && targetchannel != except) {
				SocketChannel dest = (SocketChannel) targetchannel;
				// 把数据存储到缓冲区中
				ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
				// 往通道中写数据
				dest.write(buffer);
			}
		}
	}

	private void printInfo(String str) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("[" + sdf.format(new Date()) + "]->" + str);
	}

	public static void main(String[] args) {
		new ChatServer().start();
	}

}
